package com.lianrui.core;

import static com.lianrui.message.ISystemMessage.SYS_CLASS_INSTANCE_FAILED;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.ibatis.binding.MapperProxy;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;

import com.lianrui.commons.Page;
import com.lianrui.commons.exception.BusinessException;
import com.lianrui.commons.exception.SystemException;
import com.lianrui.core.IDao.OperateType;
import com.lianrui.message.ISystemMessage;

/**
 * 功能描述：Service抽象实现类，所有的Service都需要继承该类
 * 
 * @param <List<D>> 分页类型
 * @param <D> 实体类型 【实现时必须加上】
 * @param <T> 主键（编码）类型【实现时必须加上】
 */
@Service
public abstract class AbstractService<P extends Page<D>, D, T> implements IService<P, D, T> {
	
	private static final long serialVersionUID = 1L;

	private Class<D> domainType;
	
	private Class<IDao<P, D, T>> daoType;
	
//	private SqlSessionFactory sqlSessionFactory;
	
	public AbstractService() {
		handleDomainType();
	}
	
	public Class<D> getDomainType() throws BusinessException {
		if (domainType == null) {
			handleDomainType();
		}
		return domainType;
	}

	@Override
	public Class<IDao<P, D, T>> getDaoType() throws BusinessException {
		if (daoType == null) {
			handleDaoType();
		}
		return daoType;
	}
	
//	public SqlSessionFactory getSqlSessionFactory() {
//		return sqlSessionFactory;
//	}
//	
//	@Autowired
//	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
//		this.sqlSessionFactory = sqlSessionFactory;
//	}
//	
//	public SqlSession openSession() {
//		return sqlSessionFactory.openSession();
//	}
	
	@Override
	public void add(D d) throws BusinessException {
		validate(OperateType.add, d);
		getDao().add(d);
	}

	@Override
	@SuppressWarnings("unchecked")
	public void delete(T... id) throws BusinessException {
		 getDao().delete(id);
	}

	@Override
	public void update(D d) throws BusinessException {
		validate(OperateType.update, d);
		getDao().update(d);
	}

	@Override
	public D unique(T id) throws BusinessException {
		return  getDao().unique(id);
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<D> get(T... id) throws BusinessException {
		return  getDao().get(id);
	}

	@Override
	public List<D> list(P page) throws BusinessException {
		List<D> list = getDao().list(page);
		page.setResult(list);
		page.setTotal(this.count(page));
		return list;
	}

	@Override
	public int count(P page) throws BusinessException {
		return getDao().count(page);
	}
	
	protected abstract Logger getLogger();
	
	/**
	 * 功能描述：获取Domain接口类型
	 * @throws BusinessException
	 */
	@SuppressWarnings("unchecked")
	protected void handleDomainType() {
		try {
			domainType = (Class<D>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
		} catch (Throwable e) {
			throw new SystemException(SYS_CLASS_INSTANCE_FAILED, getLogger(), e);
		}
	}
	
	/**
	 * 功能描述：获取Dao接口类型
	 * @return
	 * @throws BusinessException
	 */
	@SuppressWarnings("unchecked")
	protected void handleDaoType() throws BusinessException {
		if (getDao() instanceof Proxy) {
			InvocationHandler handler = Proxy.getInvocationHandler(getDao());
			try {
				if (handler instanceof MapperProxy) {
					Field field = FieldUtils.getDeclaredField(MapperProxy.class, "mapperInterface", true);
					daoType = (Class<IDao<P, D, T>>) field.get(handler);
				}
			} catch (Throwable e) {
				throw new SystemException(SYS_CLASS_INSTANCE_FAILED, getLogger(), e, getClass());
			}
		} else {
			daoType = (Class<IDao<P, D, T>>) getDao().getClass();
		}
		
	}
	
	protected String getStatement(String method) throws BusinessException {
		return getDao().getClass() + "." + method;
	}
	
	@Override
	public int validate(OperateType opType, D d) throws BusinessException {
		return 0;
	}
	
	@Override
	public List<Map<String, Object>> list(Map<String, Object> condition) throws BusinessException {
		return getDao().list(condition);
	}
	
	@Override
	public D pure(T id) throws BusinessException {
		throw new BusinessException(ISystemMessage.SYS_METHOD_NOTEXISTS, getLogger(), "pure");
	}
}